package chapter8;

import lombok.extern.slf4j.Slf4j;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author caiya
 * @date 2020/4/6 20:47
 * @description
 */
@Slf4j
public class SimpleDateFormatTest {
    /**
     * SimpleDateFormat是线程不安全的，是因为内部字段Calendar不是线程安全的，解决方案如下：
     * 1、每次使用时new一个新的SimpleDateFormat，缺点是对象创建开销大
     * 2、SimpleDateFormat使用时进行synchronized同步，锁可以使用SimpleDateFormat的共享对象
     * 3、使用ThreadLocal，下面演示ThreadLocal和synchronized的使用
     *
     * 4、线程池中使用ThreadLocal一定要记得get之后remove，否则核心线程会一直持有ThreadLocal的引用，导致内存泄漏
     * 5、ConcurrentHashMap使用的时候，如果value是List类型，put要使用putIfPresent，然后再存入List，否则会存在value被其它线程覆盖的问题
     */
    static ThreadLocal<DateFormat> dateFormat = ThreadLocal.withInitial(
            () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    static DateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {

        // 使用ThreadLocal解决sdf的线程安全问题
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                String date = dateFormat.get().format(new Date());
                log.info("Thread: {}, date: {}", Thread.currentThread().getName(), date);
            }).start();
        }

        // 使用锁解决sdf的线程安全问题
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    synchronized (dateFormat1) {
                        dateFormat1.parse("2020-04-06 20:56:09");
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
